/*	Remove primitiveType by add Model type
primitiveType  
    :   'Boolean'
    |   'Money' //NumberFormat.getCurrencyInstance
    |   'Percent' //NumberFormat.getPercentInstance
    |   'BigDecimal' decimalDef //NumberFormat.getNumberInstance
    |   'char'    charDef
    |   'String'
    |   'money'
    |   'Id'
    |   'Date'
    |   'DateTime'
    ;

*/
grammar Model;
options {
  output = AST;              // build trees
  ASTLabelType = ModelAST;
}

tokens {
  METHOD_DECL;
  FIELD_DECL;
  REL_DECL;
  MEMBERS;     // class body
  TYPE_DECL;
  MODF_DECL;
  ITFA_DECL;
  CLSS_DECL;
  INDX_DECL;
  IDXF_DECL;
  UNIT;
}

@header {package samples;}
@lexer::header {package samples;}

compilationUnit 
    :   (packageDeclaration)?
        (importDeclaration)*
        (typeDeclaration)+
    -> ^(UNIT typeDeclaration+)
    ;

packageDeclaration 
    :   'package' qualifiedName ';'
    ;

importDeclaration  
    :   'import' qualifiedName ';'
    ;
    
qualifiedName 
    :   IDENTIFIER ('.' IDENTIFIER)* -> IDENTIFIER+
    ;

typeDeclaration
    :   interfaceDeclaration
    |   classDeclaration
    ;

modifiers  
    :
    (   'public'
    |   'abstract'
    |	'static'
    )*
    ->  ^(MODF_DECL 'public'? 'abstract'? 'static'?)
    ;
    
interfaceDeclaration
    :   ('public')? 'interface' IDENTIFIER
        ('extends' type)?
		interfaceDeclarationBody        
    ->  ^('interface' IDENTIFIER (^('extends' type))? interfaceDeclarationBody)
    ;

classDeclaration
    :   modifiers 'class' IDENTIFIER
        ('implements' typeList)?
        classDeclarationBody
    ->  ^('class' IDENTIFIER (^('implements' typeList))? classDeclarationBody)
    ;

interfaceDeclarationBody 
    :   '{' interfaceMemberDeclaration* '}' 
    ->  ^(MEMBERS interfaceMemberDeclaration*)
    ;

classDeclarationBody 
    :   '{' memberDeclaration* '}'
    ->  ^(MEMBERS memberDeclaration*)
    ;    

interfaceMemberDeclaration
	:	fieldDeclaration
	|	methodDeclaration
	|	fieldIndexDeclaration
	;

memberDeclaration
	:	classDeclaration
	|	interfaceDeclaration
	|	interfaceMemberDeclaration
	;

fieldDeclaration
	:	fieldType IDENTIFIER '=' 'null' ';'
	-> ^(FIELD_DECL fieldType IDENTIFIER)
	;
	
methodDeclaration
	:	fieldType IDENTIFIER '(' ')' ';'
	->	^(METHOD_DECL fieldType IDENTIFIER)
	;

type
    :   qualifiedName
    ;

fieldType 
    :   type! relation[$type.tree]  
    ;

relation[CommonTree entity]
	:   ('[]') ->  ^(REL_DECL {$entity})
	|	->  ^(TYPE_DECL {$entity})
	;

fieldIndexDeclaration
	: fieldType IDENTIFIER indexList ';'
	-> ^(INDX_DECL fieldType IDENTIFIER indexList)
	;
	
indexList
	:	'=' '{'
		'"' IDENTIFIER '"' (',' '"' IDENTIFIER '"' )*
		'}'
	->	^(IDXF_DECL IDENTIFIER+)
	;


typeList 
    :   type (',' type)*
    ->  type+
    ;

/********************************************************************************************
                  Lexer section
*********************************************************************************************/
IDENTIFIER
    :   ('A'..'Z'|'a'..'z'|'0'..'9')+
    ;

WS  
    :   ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ { $channel = HIDDEN; }
    ;

COMMENT
    :   '/*' 
        (options {greedy=false;} : . )* 
        '*/' { $channel = HIDDEN; }
    ;
    
LINE_COMMENT
    :   '//' ~('\n'|'\r')*  ('\r\n' | '\r' | '\n') {$channel = HIDDEN;}
    |   '//' ~('\n'|'\r')*  {$channel = HIDDEN;} // a line comment could appear at the end of the file without CR/LF
    ;